#ifndef PYXELCORE_CONSTANTS_H_
#define PYXELCORE_CONSTANTS_H_

#include <SDL.h>
#include <SDL_image.h>

#include <algorithm>
#include <array>
#include <cfloat>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <filesystem>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <map>
#include <sstream>
#include <string>
#include <vector>

namespace pyxelcore {

//
// Common
//
const std::string VERSION = "1.4.4";
const int32_t COLOR_COUNT = 16;
const std::string WHITESPACE = " \t\v\r\n";

enum {
  COLOR_BLACK,
  COLOR_NAVY,
  COLOR_PURPLE,
  COLOR_GREEN,
  COLOR_BROWN,
  COLOR_DARKBLUE,
  COLOR_LIGHTBLUE,
  COLOR_WHITE,
  COLOR_RED,
  COLOR_ORANGE,
  COLOR_YELLOW,
  COLOR_LIME,
  COLOR_CYAN,
  COLOR_GRAY,
  COLOR_PINK,
  COLOR_PEACH,
};

//
// System
//
const int32_t MAX_SCREEN_SIZE = 256;

const int32_t MAX_FRAME_SKIP_COUNT = 9;
const int32_t MEASURE_FRAME_COUNT = 10;

const int32_t SCREEN_CAPTURE_COUNT = 900;
const int32_t SCREEN_CAPTURE_SCALE = 2;

const float MAX_WINDOW_SIZE_RATIO = 0.8f;
const int32_t WINDOW_BACKGROUND_COLOR = 0x101018;

const int32_t ICON_WIDTH = 16;
const int32_t ICON_HEIGHT = 16;
const int32_t ICON_SCALE = 4;
const std::vector<std::string> ICON_DATA = {
    "0000000110000000", "0000011F71100000", "00011FF11FF11000",
    "011FF111111FF110", "17E1111111111C71", "1E1EE111111CC1C1",
    "1E111EE11CC111C1", "1E11111E711111C1", "1E111111C11111C1",
    "1E111111C11111C1", "1E111111C11111C1", "17E11111C1111C71",
    "011EE111C11CC110", "00011EE1CCC11000", "0000011E71100000",
    "0000000110000000",
};

//
// Resource
//
const std::string RESOURCE_FILE_EXTENSION = ".pyxres";
const std::string RESOURCE_ARCHIVE_DIRNAME = "pyxel_resource/";

//
// Input
//
enum {
  KEY_SPACE,
  KEY_QUOTE,
  KEY_COMMA,
  KEY_MINUS,
  KEY_PERIOD,
  KEY_SLASH,
  KEY_0,
  KEY_1,
  KEY_2,
  KEY_3,
  KEY_4,
  KEY_5,
  KEY_6,
  KEY_7,
  KEY_8,
  KEY_9,
  KEY_SEMICOLON,
  KEY_EQUAL,
  KEY_A,
  KEY_B,
  KEY_C,
  KEY_D,
  KEY_E,
  KEY_F,
  KEY_G,
  KEY_H,
  KEY_I,
  KEY_J,
  KEY_K,
  KEY_L,
  KEY_M,
  KEY_N,
  KEY_O,
  KEY_P,
  KEY_Q,
  KEY_R,
  KEY_S,
  KEY_T,
  KEY_U,
  KEY_V,
  KEY_W,
  KEY_X,
  KEY_Y,
  KEY_Z,
  KEY_LEFT_BRACKET,
  KEY_BACKSLASH,
  KEY_RIGHT_BRACKET,
  KEY_BACKQUOTE,
  KEY_ESCAPE,
  KEY_ENTER,
  KEY_TAB,
  KEY_BACKSPACE,
  KEY_INSERT,
  KEY_DELETE,
  KEY_RIGHT,
  KEY_LEFT,
  KEY_DOWN,
  KEY_UP,
  KEY_PAGE_UP,
  KEY_PAGE_DOWN,
  KEY_HOME,
  KEY_END,
  KEY_CAPS_LOCK,
  KEY_SCROLL_LOCK,
  KEY_NUM_LOCK,
  KEY_PRINT_SCREEN,
  KEY_PAUSE,
  KEY_F1,
  KEY_F2,
  KEY_F3,
  KEY_F4,
  KEY_F5,
  KEY_F6,
  KEY_F7,
  KEY_F8,
  KEY_F9,
  KEY_F10,
  KEY_F11,
  KEY_F12,
  KEY_KP_0,
  KEY_KP_1,
  KEY_KP_2,
  KEY_KP_3,
  KEY_KP_4,
  KEY_KP_5,
  KEY_KP_6,
  KEY_KP_7,
  KEY_KP_8,
  KEY_KP_9,
  KEY_KP_DECIMAL,
  KEY_KP_DIVIDE,
  KEY_KP_MULTIPLY,
  KEY_KP_SUBTRACT,
  KEY_KP_ADD,
  KEY_KP_ENTER,
  KEY_KP_EQUAL,
  KEY_LEFT_SHIFT,
  KEY_LEFT_CONTROL,
  KEY_LEFT_ALT,
  KEY_LEFT_SUPER,
  KEY_RIGHT_SHIFT,
  KEY_RIGHT_CONTROL,
  KEY_RIGHT_ALT,
  KEY_RIGHT_SUPER,
  KEY_MENU,

  SDL_KEYCODE_COUNT,

  KEY_SHIFT = SDL_KEYCODE_COUNT,
  KEY_CONTROL,
  KEY_ALT,
  KEY_SUPER,
  KEY_NONE,

  MOUSE_LEFT_BUTTON,
  MOUSE_MIDDLE_BUTTON,
  MOUSE_RIGHT_BUTTON,

  GAMEPAD_1_A,
  GAMEPAD_1_B,
  GAMEPAD_1_X,
  GAMEPAD_1_Y,
  GAMEPAD_1_LEFT_SHOULDER,
  GAMEPAD_1_RIGHT_SHOULDER,
  GAMEPAD_1_SELECT,
  GAMEPAD_1_START,
  GAMEPAD_1_UP,
  GAMEPAD_1_RIGHT,
  GAMEPAD_1_DOWN,
  GAMEPAD_1_LEFT,

  GAMEPAD_2_A,
  GAMEPAD_2_B,
  GAMEPAD_2_X,
  GAMEPAD_2_Y,
  GAMEPAD_2_LEFT_SHOULDER,
  GAMEPAD_2_RIGHT_SHOULDER,
  GAMEPAD_2_SELECT,
  GAMEPAD_2_START,
  GAMEPAD_2_UP,
  GAMEPAD_2_RIGHT,
  GAMEPAD_2_DOWN,
  GAMEPAD_2_LEFT,

  KEY_COUNT,
};

const uint32_t BUTTON_COUNT = GAMEPAD_1_LEFT - GAMEPAD_1_A + 1;

const uint32_t SDL_KEYCODE_TABLE[SDL_KEYCODE_COUNT] = {
    SDLK_SPACE,
    SDLK_QUOTE,
    SDLK_COMMA,
    SDLK_MINUS,
    SDLK_PERIOD,
    SDLK_SLASH,
    SDLK_0,
    SDLK_1,
    SDLK_2,
    SDLK_3,
    SDLK_4,
    SDLK_5,
    SDLK_6,
    SDLK_7,
    SDLK_8,
    SDLK_9,
    SDLK_SEMICOLON,
    SDLK_EQUALS,
    SDLK_a,
    SDLK_b,
    SDLK_c,
    SDLK_d,
    SDLK_e,
    SDLK_f,
    SDLK_g,
    SDLK_h,
    SDLK_i,
    SDLK_j,
    SDLK_k,
    SDLK_l,
    SDLK_m,
    SDLK_n,
    SDLK_o,
    SDLK_p,
    SDLK_q,
    SDLK_r,
    SDLK_s,
    SDLK_t,
    SDLK_u,
    SDLK_v,
    SDLK_w,
    SDLK_x,
    SDLK_y,
    SDLK_z,
    SDLK_LEFTBRACKET,
    SDLK_BACKSLASH,
    SDLK_RIGHTBRACKET,
    SDLK_BACKQUOTE,
    SDLK_ESCAPE,
    SDLK_RETURN,
    SDLK_TAB,
    SDLK_BACKSPACE,
    SDLK_INSERT,
    SDLK_DELETE,
    SDLK_RIGHT,
    SDLK_LEFT,
    SDLK_DOWN,
    SDLK_UP,
    SDLK_PAGEUP,
    SDLK_PAGEDOWN,
    SDLK_HOME,
    SDLK_END,
    SDLK_CAPSLOCK,
    SDLK_SCROLLLOCK,
    SDLK_NUMLOCKCLEAR,
    SDLK_PRINTSCREEN,
    SDLK_PAUSE,
    SDLK_F1,
    SDLK_F2,
    SDLK_F3,
    SDLK_F4,
    SDLK_F5,
    SDLK_F6,
    SDLK_F7,
    SDLK_F8,
    SDLK_F9,
    SDLK_F10,
    SDLK_F11,
    SDLK_F12,
    SDLK_KP_0,
    SDLK_KP_1,
    SDLK_KP_2,
    SDLK_KP_3,
    SDLK_KP_4,
    SDLK_KP_5,
    SDLK_KP_6,
    SDLK_KP_7,
    SDLK_KP_8,
    SDLK_KP_9,
    SDLK_KP_DECIMAL,
    SDLK_KP_DIVIDE,
    SDLK_KP_MULTIPLY,
    SDLK_KP_MINUS,
    SDLK_KP_PLUS,
    SDLK_KP_ENTER,
    SDLK_KP_EQUALS,
    SDLK_LSHIFT,
    SDLK_LCTRL,
    SDLK_LALT,
    SDLK_LGUI,
    SDLK_RSHIFT,
    SDLK_RCTRL,
    SDLK_RALT,
    SDLK_RGUI,
    SDLK_MENU,
};

const SDL_GameControllerButton SDL_BUTTON_TABLE[BUTTON_COUNT] = {
    SDL_CONTROLLER_BUTTON_A,
    SDL_CONTROLLER_BUTTON_B,
    SDL_CONTROLLER_BUTTON_X,
    SDL_CONTROLLER_BUTTON_Y,
    SDL_CONTROLLER_BUTTON_LEFTSHOULDER,
    SDL_CONTROLLER_BUTTON_RIGHTSHOULDER,
    SDL_CONTROLLER_BUTTON_BACK,
    SDL_CONTROLLER_BUTTON_START,
    SDL_CONTROLLER_BUTTON_DPAD_UP,
    SDL_CONTROLLER_BUTTON_DPAD_RIGHT,
    SDL_CONTROLLER_BUTTON_DPAD_DOWN,
    SDL_CONTROLLER_BUTTON_DPAD_LEFT,
};

//
// Graphics
//
const int32_t USER_IMAGE_BANK_COUNT = 3;
const int32_t TOTAL_IMAGE_BANK_COUNT = USER_IMAGE_BANK_COUNT + 2;
const int32_t IMAGE_BANK_FOR_SYSTEM = USER_IMAGE_BANK_COUNT;
const int32_t IMAGE_BANK_FOR_SCREEN = USER_IMAGE_BANK_COUNT + 1;
const int32_t IMAGE_BANK_WIDTH = MAX_SCREEN_SIZE;
const int32_t IMAGE_BANK_HEIGHT = MAX_SCREEN_SIZE;

const int32_t TILEMAP_BANK_COUNT = 8;
const int32_t TILEMAP_BANK_WIDTH = 256;
const int32_t TILEMAP_BANK_HEIGHT = 256;
const int32_t TILEMAP_CHIP_WIDTH = 8;
const int32_t TILEMAP_CHIP_HEIGHT = 8;
const int32_t TILEMAP_CHIP_COUNT = (TILEMAP_BANK_WIDTH / TILEMAP_CHIP_WIDTH) *
                                   (TILEMAP_BANK_HEIGHT / TILEMAP_CHIP_HEIGHT);

const int32_t MOUSE_CURSOR_X = 2;
const int32_t MOUSE_CURSOR_Y = 2;
const int32_t MOUSE_CURSOR_WIDTH = 8;
const int32_t MOUSE_CURSOR_HEIGHT = 8;
const std::vector<std::string> MOUSE_CURSOR_DATA = {
    "00000011", "07776011", "07760111", "07676011",
    "06067601", "00106760", "11110601", "11111011",
};

const int32_t MIN_FONT_CODE = 32;
const int32_t MAX_FONT_CODE = 127;
const int32_t FONT_X = 12;
const int32_t FONT_Y = 0;
const int32_t FONT_WIDTH = 4;
const int32_t FONT_HEIGHT = 6;
const int32_t FONT_ROW_COUNT = 48;
const int32_t FONT_COLOR = 7;
const std::vector<uint32_t> FONT_DATA = {
    0x000000, 0x444040, 0xAA0000, 0xAEAEA0, 0x6C6C40, 0x824820, 0x4A4AC0,
    0x440000, 0x244420, 0x844480, 0xA4E4A0, 0x04E400, 0x000480, 0x00E000,
    0x000040, 0x224880, 0x6AAAC0, 0x4C4440, 0xC248E0, 0xC242C0, 0xAAE220,
    0xE8C2C0, 0x68EAE0, 0xE24880, 0xEAEAE0, 0xEAE2C0, 0x040400, 0x040480,
    0x248420, 0x0E0E00, 0x842480, 0xE24040, 0x4AA860, 0x4AEAA0, 0xCACAC0,
    0x688860, 0xCAAAC0, 0xE8E8E0, 0xE8E880, 0x68EA60, 0xAAEAA0, 0xE444E0,
    0x222A40, 0xAACAA0, 0x8888E0, 0xAEEAA0, 0xCAAAA0, 0x4AAA40, 0xCAC880,
    0x4AAE60, 0xCAECA0, 0x6842C0, 0xE44440, 0xAAAA60, 0xAAAA40, 0xAAEEA0,
    0xAA4AA0, 0xAA4440, 0xE248E0, 0x644460, 0x884220, 0xC444C0, 0x4A0000,
    0x0000E0, 0x840000, 0x06AA60, 0x8CAAC0, 0x068860, 0x26AA60, 0x06AC60,
    0x24E440, 0x06AE24, 0x8CAAA0, 0x404440, 0x2022A4, 0x8ACCA0, 0xC444E0,
    0x0EEEA0, 0x0CAAA0, 0x04AA40, 0x0CAAC8, 0x06AA62, 0x068880, 0x06C6C0,
    0x4E4460, 0x0AAA60, 0x0AAA40, 0x0AAEE0, 0x0A44A0, 0x0AA624, 0x0E24E0,
    0x64C460, 0x444440, 0xC464C0, 0x6C0000, 0xEEEEE0,
};

//
// Audio
//
const int32_t AUDIO_SAMPLE_RATE = 22050;
const int32_t AUDIO_BLOCK_SIZE = 2048;
const int32_t AUDIO_ONE_SPEED = AUDIO_SAMPLE_RATE / 120;
const int32_t AUDIO_ONE_VOLUME = 0x7FFF / (4 * 7);

const int32_t USER_SOUND_BANK_COUNT = 64;
const int32_t TOTAL_SOUND_BANK_COUNT = USER_SOUND_BANK_COUNT + 1;
const int32_t SOUND_BANK_FOR_SYSTEM = USER_SOUND_BANK_COUNT;

const int32_t MUSIC_BANK_COUNT = 8;

//
// Sound class
//
enum {
  TONE_TRIANGLE,
  TONE_SQUARE,
  TONE_PULSE,
  TONE_NOISE,
};

enum {
  EFFECT_NONE,
  EFFECT_SLIDE,
  EFFECT_VIBRATO,
  EFFECT_FADEOUT,
};

const int32_t INITIAL_SOUND_SPEED = 30;

//
// Music class
//
const int32_t MUSIC_CHANNEL_COUNT = 4;

// Default parameters
const std::string DEFAULT_CAPTION = "Pyxel";
const int32_t DEFAULT_SCALE = 0;
const int32_t DEFAULT_FPS = 30;
const int32_t DEFAULT_QUIT_KEY = KEY_ESCAPE;
const std::array<int32_t, COLOR_COUNT> DEFAULT_PALETTE = {
    0x000000, 0x2B335F, 0x7E2072, 0x19959C, 0x8B4852, 0x395C98,
    0xA9C1FF, 0xEEEEEE, 0xD4186C, 0xD38441, 0xE9C35B, 0x70C6A9,
    0x7696DE, 0xA3A3A3, 0xFF9798, 0xEDC7B0,
};

//
// Helper functions
//
int32_t GetConstantNumber(const std::string& name);
std::string GetConstantString(const std::string& name);

}  // namespace pyxelcore

#endif  // PYXELCORE_CONSTANTS_H_
